//
//  GuidePageView.swift
//  GuidePageView
//
//  Created by lisilong on 2018/1/4.
//  Copyright © 2018年 tuandai. All rights reserved.
//

import UIKit
import Kingfisher
public class GuidePageView: UIView {
    
    private lazy var guideScrollView: UIScrollView = {
        let view = UIScrollView.init()
        view.backgroundColor = UIColor.clear
        view.bounces = false
        view.isPagingEnabled = true
        view.showsHorizontalScrollIndicator = false
        view.delegate = self
        return view
    }()
    
    
    /// 指示器
    public var pageControl: GuidePageControl!
    
    /// 是否打开右滑进入主题，default: false
    public var isSlipIntoHomeView: Bool = false
    
    ///是否显示 controll
    private var isShowControll:Bool = false
    
    /// 数据源
    private var imageArray: Array<String>?
    

    public let pageControlHeight: CGFloat = 40.0
    public let startHeigth: CGFloat = 40.0
    public let loginHeight: CGFloat = 78*PionHeight
    
    public let bottomHeight:CGFloat = 90*PionHeight+MARGIN_BOTTOM+20*PionHeight+56*PionHeight
    public let topHeight:CGFloat = 68*PionHeight+STATUSBAR_HEIGHT
    
    
    /// 是否正在做滑入动作
    private var isSliping: Bool = false
    
    // MARK: - life cycle
    private override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    /// App启动引导页
    ///
    /// - Parameters:
    ///   - frame: 引导页大小
    ///   - images: 引导页图片（gif/png/jpeg...）注意：gif图不可放在Assets中，否则加载不出来（建议引导页的图片都不要放在Assets文件中，因为使用imageName加载时，系统会缓存图片，造成内存暴增）
    ///   - isHiddenSkipBtn: 是否隐藏跳过按钮
    ///   - isHiddenStartBtn: 是否隐藏立即体验按钮
    ///   - loginRegistCompletion: 登录/注册回调
    ///   - startCompletion: 立即体验回调
    public convenience init(frame: CGRect = UIScreen.main.bounds,
                            images: Array<String>,
                            isShowControll:Bool=true) {
        self.init(frame: frame)
        
        self.imageArray       = images
        self.isShowControll = isShowControll
        setupSubviews(frame: frame)
        self.backgroundColor = UIColor.white
    }
    
    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - setup
    private func setupSubviews(frame: CGRect) {
        let size = UIScreen.main.bounds.size
        guideScrollView.frame = frame
        guideScrollView.contentSize = CGSize.init(width: frame.size.width * CGFloat(imageArray?.count ?? 0), height: frame.size.height)
        self.addSubview(guideScrollView)
        
        guard imageArray != nil, imageArray?.count ?? 0 > 0 else { return }
        for index in 0..<(imageArray?.count ?? 1) {
            let name        = imageArray![index]
            let imageFrame  = CGRect.init(x: size.width * CGFloat(index)+15*PionWidth, y: topHeight, width: size.width-30*PionWidth, height: ScreenH-topHeight-bottomHeight)
            var view: UIView
            
            let filePath    = Bundle.main.path(forResource: name, ofType: nil) ?? ""
            let data: Data? = try? Data.init(contentsOf: URL.init(fileURLWithPath: filePath), options: Data.ReadingOptions.uncached)
            
            let type = GifImageOperation.checkDataType(data: data)
            if type == DataType.gif {   // gif
                view = GifImageOperation.init(frame: imageFrame, gifData: data!)
            } else {                    // 其它图片
                // Warning: 假如说图片是放在Assets中的，使用Bundle的方式加载不到，需要使用init(named:)方法加载。
                view = UIImageView.init(frame: imageFrame)
                view.contentMode = .scaleToFill
                view.clipsToBounds = true
                if name.contains("http") || name.contains("https"){
                    (view as! UIImageView).kf.setImage(with: URL(string: name))
                }else{
                    (view as! UIImageView).image = (data != nil ? UIImage.init(data: data!) : UIImage.init(named: name))
                }
                
            }
            guideScrollView.addSubview(view)
        }
        
        ///是否 显示 底部的控制器
        if isShowControll{
            pageControl=GuidePageControl(frame: CGRect(x: 0, y: size.height  - bottomHeight  + 10*PionHeight  , width: ScreenW, height: 10*PionHeight))
            pageControl.setImage(UIImage.image(withColor: UIColor.RGB(r: 14, g: 185, b: 141)!, size: CGSize(width: 5, height: 5)), for: .selected)
            pageControl.setImage(UIImage.image(withColor: UIColor.RGBWithAlpha(r: 14, g: 185, b: 141, al: 0.3)!, size: CGSize(width: 5, height: 5)), for: .normal)
            pageControl.contentHorizontalAlignment = .center
            pageControl.numberOfPages=imageArray?.count ?? 0
            self.addSubview(pageControl)
        }
        
        
    }
    
    // MARK: - actions
    private func removeGuideViewFromSupview() {
        UIView.animate(withDuration: 1.0, delay: 0, options: UIView.AnimationOptions.curveEaseOut, animations: {
            self.alpha = 0.0
        }) { (_) in
            self.removeFromSuperview()
        }
    }
    
    
    /// 作为 pod 第三方库取图片资源
    ///
    /// - Parameter name: 图片名
    /// - Returns: 图片
    private func imageFromBundle(name: String) -> UIImage {
        let podBundle = Bundle(for: self.classForCoder)
        let bundleURL = podBundle.url(forResource: "GuideImage", withExtension: "bundle")
        let bundle = Bundle(url: bundleURL!)
        let image = UIImage(named: String(name), in: bundle, compatibleWith: nil)
        return image!
    }
    
    /// 根据UIColor创建UIImage
    ///
    /// - Parameters:
    ///   - color: 颜色
    ///   - size: 图片大小
    /// - Returns: 图片
    private func creatImage(color: UIColor, size: CGSize = CGSize.init(width: 100, height: 100)) -> UIImage {
        let size = (size == CGSize.zero ? CGSize(width: 100, height: 100): size)
        UIGraphicsBeginImageContext(size)
        let context = UIGraphicsGetCurrentContext()
        context!.setFillColor(color.cgColor)
        context!.fill(CGRect.init(x: 0, y: 0, width: size.width, height: size.height))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image!
    }
}

// MARK: - <UIScrollViewDelegate>
extension GuidePageView: UIScrollViewDelegate {
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard isSlipIntoHomeView else { return }
        guard isSliping == false else { return }
        
        let totalWidth = UIScreen.main.bounds.size.width * CGFloat((imageArray?.count ?? 1) - 1)
        let offsetX = scrollView.contentOffset.x - totalWidth
        if offsetX > 30 {
            isSliping = true
            UIView.animate(withDuration: 1.0, animations: {
                self.guideScrollView.alpha = 0.0
                var frame = self.guideScrollView.frame
                frame.origin.x = -UIScreen.main.bounds.size.width
                self.guideScrollView.frame = frame
            }) { (_) in
                self.isSliping = false
            }
        }
    }
    
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let page: Int = Int(scrollView.contentOffset.x / scrollView.bounds.size.width)
        // 设置指示器
        pageControl.currentPage = page
    }
}



@IBDesignable
open class GuidePageControl: UIControl {
    
    /// The number of page indicators of the page control. Default is 0.
    @IBInspectable
    open var numberOfPages: Int = 0 {
        didSet {
            self.setNeedsCreateIndicators()
        }
    }
    
    /// The current page, highlighted by the page control. Default is 0.
    @IBInspectable
    open var currentPage: Int = 0 {
        didSet {
            self.setNeedsUpdateIndicators()
        }
    }
    
    /// The spacing to use of page indicators in the page control.
    @IBInspectable
    open var itemSpacing: CGFloat = 6 {
        didSet {
            self.setNeedsUpdateIndicators()
        }
    }
    
    /// The spacing to use between page indicators in the page control.
    @IBInspectable
    open var interitemSpacing: CGFloat = 6 {
        didSet {
            self.setNeedsLayout()
        }
    }
    
    /// The distance that the page indicators is inset from the enclosing page control.
    /// @IBInspectable 不支持 UIEdgeInsets 类型
    open var contentInsets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0) {
        didSet {
            self.setNeedsLayout()
        }
    }
    
    /// The horizontal alignment of content within the control’s bounds. Default is center.
    open override var contentHorizontalAlignment: UIControl.ContentHorizontalAlignment {
        didSet {
            self.setNeedsLayout()
        }
    }
    
    internal var strokeColors: [UIControl.State: UIColor] = [:]
    internal var fillColors: [UIControl.State: UIColor] = [:]
    internal var paths: [UIControl.State: UIBezierPath] = [:]
    internal var images: [UIControl.State: UIImage] = [:]
    internal var alphas: [UIControl.State: CGFloat] = [:]
    internal var transforms: [UIControl.State: CGAffineTransform] = [:]
    
    fileprivate weak var contentView: UIView!
    
    fileprivate var needsUpdateIndicators = false
    fileprivate var needsCreateIndicators = false
    fileprivate var indicatorLayers = [CAShapeLayer]()
    
    fileprivate var runLoopObserver: CFRunLoopObserver?
    fileprivate var runLoopCallback: CFRunLoopObserverCallBack = {
        (observer: CFRunLoopObserver?, activity: CFRunLoopActivity, info: UnsafeMutableRawPointer?) -> Void in
        guard let info = info else {
            return
        }
        let pageControl = Unmanaged<GuidePageControl>.fromOpaque(info).takeUnretainedValue()
        pageControl.createIndicatorsIfNecessary()
        pageControl.updateIndicatorsIfNecessary()
    }
    
    public override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    
    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    
    deinit {
        CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), self.runLoopObserver, .commonModes)
    }
    
    open override func layoutSubviews() {
        super.layoutSubviews()
        self.contentView.frame = {
            let x = self.contentInsets.left
            let y = self.contentInsets.top
            let width = self.frame.width - self.contentInsets.left - self.contentInsets.right
            let height = self.frame.height - self.contentInsets.top - self.contentInsets.bottom
            let frame = CGRect(x: x, y: y, width: width, height: height)
            return frame
        }()
    }
    
    open override func layoutSublayers(of layer: CALayer) {
        super.layoutSublayers(of: layer)
        
        let diameter = self.itemSpacing
        let spacing = self.interitemSpacing
        var x: CGFloat = {
            switch self.contentHorizontalAlignment {
            case .left:
                return 0
            case .center, .fill:
                let midX = self.contentView.bounds.midX
                let amplitude = CGFloat(self.numberOfPages/2) * diameter + spacing*CGFloat((self.numberOfPages-1)/2)
                return midX - amplitude
            case .right:
                let contentWidth = diameter*CGFloat(self.numberOfPages) + CGFloat(self.numberOfPages-1)*spacing
                return contentView.frame.width - contentWidth
            case .leading:
                return 0
            case .trailing:
                return 0
            default:
                return 0
            }
        }()
        for (index, value) in self.indicatorLayers.enumerated() {
            let state: UIControl.State = (index == self.currentPage) ? .selected : .normal
            let image = self.images[state]
            let size = image?.size ?? CGSize(width: diameter, height: diameter)
            let origin = CGPoint(x: x - (size.width-diameter)*0.5, y: self.contentView.bounds.midY-size.height*0.5)
            value.frame = CGRect(origin: origin, size: size)
            x += (spacing + diameter)
        }
        
    }
    
    /// Sets the stroke color for page indicators to use for the specified state. (selected/normal).
    ///
    /// - Parameters:
    ///   - strokeColor: The stroke color to use for the specified state.
    ///   - state: The state that uses the specified stroke color.
    @objc(setStrokeColor:forState:)
    open func setStrokeColor(_ strokeColor: UIColor?, for state: UIControl.State) {
        guard self.strokeColors[state] != strokeColor else {
            return
        }
        self.strokeColors[state] = strokeColor
        self.setNeedsUpdateIndicators()
    }
    
    /// Sets the fill color for page indicators to use for the specified state. (selected/normal).
    ///
    /// - Parameters:
    ///   - fillColor: The fill color to use for the specified state.
    ///   - state: The state that uses the specified fill color.
    @objc(setFillColor:forState:)
    open func setFillColor(_ fillColor: UIColor?, for state: UIControl.State) {
        guard self.fillColors[state] != fillColor else {
            return
        }
        self.fillColors[state] = fillColor
        self.setNeedsUpdateIndicators()
    }
    
    /// Sets the image for page indicators to use for the specified state. (selected/normal).
    ///
    /// - Parameters:
    ///   - image: The image to use for the specified state.
    ///   - state: The state that uses the specified image.
    @objc(setImage:forState:)
    open func setImage(_ image: UIImage?, for state: UIControl.State) {
        guard self.images[state] != image else {
            return
        }
        self.images[state] = image
        self.setNeedsUpdateIndicators()
    }
    
    @objc(setAlpha:forState:)
    
    /// Sets the alpha value for page indicators to use for the specified state. (selected/normal).
    ///
    /// - Parameters:
    ///   - alpha: The alpha value to use for the specified state.
    ///   - state: The state that uses the specified alpha.
    open func setAlpha(_ alpha: CGFloat, for state: UIControl.State) {
        guard self.alphas[state] != alpha else {
            return
        }
        self.alphas[state] = alpha
        self.setNeedsUpdateIndicators()
    }
    
    /// Sets the path for page indicators to use for the specified state. (selected/normal).
    ///
    /// - Parameters:
    ///   - path: The path to use for the specified state.
    ///   - state: The state that uses the specified path.
    @objc(setPath:forState:)
    open func setPath(_ path: UIBezierPath?, for state: UIControl.State) {
        guard self.paths[state] != path else {
            return
        }
        self.paths[state] = path
        self.setNeedsUpdateIndicators()
    }
    
    // MARK: - Private functions
    
    fileprivate func commonInit() {
        
        // Content View
        let view = UIView(frame: .zero)
        view.backgroundColor = UIColor.clear
        self.addSubview(view)
        self.contentView = view
        
        // RunLoop
        let runLoop = CFRunLoopGetCurrent()
        let activities: CFRunLoopActivity = [.entry, .afterWaiting]
        var context = CFRunLoopObserverContext(version: 0, info: Unmanaged.passUnretained(self).toOpaque(), retain: nil, release: nil, copyDescription: nil)
        self.runLoopObserver = CFRunLoopObserverCreate(nil, activities.rawValue, true, Int.max, self.runLoopCallback, &context)
        CFRunLoopAddObserver(runLoop, self.runLoopObserver, .commonModes)
        
    }
    
    fileprivate func setNeedsUpdateIndicators() {
        self.needsUpdateIndicators = true
        self.setNeedsLayout()
    }
    
    fileprivate func updateIndicatorsIfNecessary() {
        guard self.needsUpdateIndicators else {
            return
        }
        guard !self.indicatorLayers.isEmpty else {
            return
        }
        self.needsUpdateIndicators = false
        self.indicatorLayers.forEach { (layer) in
            self.updateIndicatorAttributes(for: layer)
        }
    }
    
    fileprivate func updateIndicatorAttributes(for layer: CAShapeLayer) {
        let index = self.indicatorLayers.firstIndex(of: layer)
        let state: UIControl.State = index == self.currentPage ? .selected : .normal
        if let image = self.images[state] {
            layer.strokeColor = nil
            layer.fillColor = nil
            layer.path = nil
            layer.cornerRadius=image.size.width/2
            layer.masksToBounds=true
            layer.contents = image.cgImage
        } else {
            layer.contents = nil
            let strokeColor = self.strokeColors[state]
            let fillColor = self.fillColors[state]
            if strokeColor == nil && fillColor == nil {
                layer.fillColor = (state == .selected ? UIColor.white : UIColor.gray).cgColor
                layer.strokeColor = nil
            } else {
                layer.strokeColor = strokeColor?.cgColor
                layer.fillColor = fillColor?.cgColor
            }
            layer.path = self.paths[state]?.cgPath ?? UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: self.itemSpacing, height: self.itemSpacing)).cgPath
        }
        if let transform = self.transforms[state] {
            layer.transform = CATransform3DMakeAffineTransform(transform)
        }
        layer.opacity = Float(self.alphas[state] ?? 1.0)
    }
    
    fileprivate func setNeedsCreateIndicators() {
        self.needsCreateIndicators = true
    }
    
    fileprivate func createIndicatorsIfNecessary() {
        guard self.needsCreateIndicators else {
            return
        }
        self.needsCreateIndicators = false
        self.indicatorLayers.forEach { (layer) in
            layer.removeFromSuperlayer()
        }
        self.indicatorLayers.removeAll()
        for _ in 0..<self.numberOfPages {
            let layer = CAShapeLayer()
            layer.actions = ["bounds": NSNull()]
            self.contentView.layer.addSublayer(layer)
            self.indicatorLayers.append(layer)
        }
        self.setNeedsUpdateIndicators()
        self.updateIndicatorsIfNecessary()
    }
    
}

extension UIControl.State: Hashable {
    public var hashValue: Int {
        return Int((6777*self.rawValue+3777)%UInt(UInt16.max))
    }
}
